//
//  HROTAManager.swift
//  APIHeartRate
//
//  Created by Tek on 2023/3/12.
//

import UIKit
import CoreBluetooth
//import CommonCrypto

public enum OtaError: Error, Comparable {
    case invalidFile
    case interputed
    case invalidResponse
    case reSend(Int,Int)
}

public enum OtaStatus: Comparable {
case start(Int)
case writeStart
case writeEnd
case erase
case inProgress(Int,Int)
case finish
case failed
case compete
}

protocol HROTADelegate {
    func sendNext(data: Data)
    func readOtaChar()
    func otaError(_ error: OtaError)
    func otaLog(_ status: OtaStatus, _ progress: Float)
}

class HROTAManager: NSObject {
    
    static let share = HROTAManager()
    
    private var delegate: HROTADelegate?
    
    var ReadData = Data()
    private var SendPacketID:Int = 0
    private var SendPacketAllNum:Int = 0
    private var SendSectionID:Int = 0
    var SECTION_CRC:Int = 0
    var CRC:Int = 0
    let CMD_FW_WRITE_START:UInt8 = 0x14
    let CMD_FW_UPGRADEV20:UInt8 = 0x15
    let CMD_FW_ERASE:UInt8 = 0x16
    
    let MAX_TRANS_COUNT_V30 = 20
    let MAX_TRANS_SECTIONALL_COUNT = 5120
    let MAX_TRANS_SECTIONALL_PACKET_COUNT = (5120 / 20)
    var MAX_TRANS_SECTIONALL_SIZE = 5120
    
    let ACTIONTYPE_CMD_FW_WRITE_START:UInt8 = 0x01
    let ACTIONTYPE_CMD_FW_WRITE_END:UInt8 = 0x02
    let ACTIONTYPE_CMD_FW_ERASE:UInt8 = 0x03
    let ACTIONTYPE_CMD_FW_UPGRADE:UInt8 = 0x05
    let ACTIONTYPE_CMD_FW_FINISH:UInt8 = 0x09
    let ERR_COMMAND_FAILED:UInt8 = 0x01
    let EVT_COMMAND_COMPLETE:UInt8 = 0x0e
    var isCheckCRC = false
    var actionType:UInt8 = 0
    /// 是否正在升级
    var isUpgrade = false
    var isErase = true
    var shouldAbort = false
    
    public func setOtaDelegate(del: HROTADelegate){
        delegate = del
    }
    public func startOtaWithResouce(data: Data) {
        clearAll()
        upgrade(binData: data)
    }
    
    public func abortOTA() {
        shouldAbort = true
    }
    
    private func clearAll() {
        isCheckCRC = false
        actionType = 0
        isUpgrade = false
        shouldAbort = false
        isErase = true
        SECTION_CRC = 0
        CRC = 0
        SendPacketID = 0
        SendPacketAllNum = 0
        SendSectionID = 0
        MAX_TRANS_SECTIONALL_SIZE = 5120
    }
    
    /// 开始升级
    fileprivate func upgrade(binData: Data){
        if binData.count ==  0{
            delegate?.otaError(.invalidFile)
            return
        }
        let size = binData.count/MemoryLayout.size(ofValue: UInt8())
        delegate?.otaLog(.start(size), 0)
        self.OTA_Process_Start(OTAData: binData)
    }
    ///开始OTA
    fileprivate func OTA_Process_Start(OTAData:Data){
        
        if (OTAData.count == 0) {
            delegate?.otaError(.invalidFile)
            return
        }
//
//        self.ReadData = Data(OTAData.dropFirst(6))
//        let md5DataStr = self.ReadData.md5Hash()
//        let halfStr = md5DataStr.suffix(4)
//
//        guard let strFromBin = String.init(data: OTAData[Range(0...5)], encoding: .utf8), strFromBin == "AC\(halfStr)" else {
//            delegate?.otaError(.invalidFile)
//            return
//        }
        
    
        ReadData = OTAData
    
        self.isUpgrade = true
        SendSectionID = 0
        for i in 0..<OTAData.count {
            var CC = OTAData[i]
            CC &= 0x000000FF
            CRC += Int(CC)
            CRC = CRC & 0x0000FFFF
        }
        DispatchQueue.main.asyncAfter(deadline:.now() + 1.0, execute: {
            self.OTA_Erase_Flash()
        })
       
    }
    fileprivate func OTA_Erase_Flash(){
        var charArray=[UInt8](repeating:0, count:2)
        charArray[0] = CMD_FW_ERASE
        charArray[1] = 0x00
        self.actionType = ACTIONTYPE_CMD_FW_ERASE
        let writeData = NSData(bytes: &charArray, length: charArray.count) as Data
        delegate?.sendNext(data: writeData)
        self.isCheckCRC = true
        delegate?.otaLog(.erase, 0.0)
        DispatchQueue.main.asyncAfter(deadline:.now() + 3.0, execute: {
            self.sendNextData()
        })
    }
    fileprivate func  OTA_Process_doing(data:Data){
        if (actionType == ACTIONTYPE_CMD_FW_ERASE) {
            OTA_Write_Flash_Start_V30(data:data)
        } else if (actionType == ACTIONTYPE_CMD_FW_WRITE_START) {
            OTA_Write_Section_All_Flash()
        } else if (actionType == ACTIONTYPE_CMD_FW_WRITE_END) {
            OTA_Write_Flash_Continue_V30(data: data)
        } else if (actionType == ACTIONTYPE_CMD_FW_UPGRADE) {
            OTA_Upgrade_Flash_V30(Size: ReadData.count, CRC: CRC)
        } else if (actionType == ACTIONTYPE_CMD_FW_FINISH) {
            if ( data.count > 0 && (data[0] == EVT_COMMAND_COMPLETE) && (data[3] == ERR_COMMAND_FAILED)) {//ota升级失败
                self.ota_failed()
            } else {
                print("升级完成===")
                self.isUpgrade = false
                delegate?.otaLog(.compete, 1)
            }
        }
    }
    
    fileprivate func OTA_Write_Flash_Start_V30(data:Data){
        SendPacketAllNum = ReadData.count / MAX_TRANS_COUNT_V30
        if (ReadData.count % MAX_TRANS_COUNT_V30 != 0){
            SendPacketAllNum += 1
        }
        OTA_Write_Flash_Continue_V30(data:data)
        delegate?.otaLog(.writeStart, 0.0)
    }
    
    fileprivate func OTA_Write_Flash_Continue_V30(data:Data){
        actionType = ACTIONTYPE_CMD_FW_WRITE_START
        if data.count != 0 {
            if SendPacketID != 0 {
                if data.count >= 8{
                    let check = (((Int(data[7]) & 0x000000ff) << 8) | (Int(data[6]) & 0x000000ff))
                    
                    if ((check & 0x0000ffff) != (SECTION_CRC & 0x0000ffff)) {
                        delegate?.otaError(.reSend(SendSectionID,SendPacketID))
                        SendSectionID -= 1;
                        SendPacketID = MAX_TRANS_SECTIONALL_PACKET_COUNT * SendSectionID;
                    }
                }
            }
        }
        if ((SendPacketAllNum - SendPacketID) >= MAX_TRANS_SECTIONALL_PACKET_COUNT){
            MAX_TRANS_SECTIONALL_SIZE = MAX_TRANS_SECTIONALL_COUNT
        }else{
            MAX_TRANS_SECTIONALL_SIZE = ReadData.count % MAX_TRANS_SECTIONALL_COUNT
        }
        SECTION_CRC = 0
        for i in 0..<MAX_TRANS_SECTIONALL_SIZE{
            var CC = ReadData[SendSectionID * MAX_TRANS_SECTIONALL_COUNT + i]
            CC &= 0x000000FF
            SECTION_CRC += Int(CC)
        }
        OTA_Write_Flash_section_start(check: SECTION_CRC, size: MAX_TRANS_SECTIONALL_SIZE, Address: SendSectionID)
        SendSectionID += 1
        self.isCheckCRC = false
    }
    fileprivate func OTA_Write_Flash_section_start(check:Int,size:Int,Address:Int){
        var WriteData=[UInt8](repeating:0, count:10)
        let address = Int64(Address * MAX_TRANS_SECTIONALL_COUNT)
        WriteData[0] = CMD_FW_WRITE_START
        WriteData[1] = 0x13
        WriteData[2] = UInt8(address & 0x000000FF)
        WriteData[3] = UInt8((address & 0x0000FF00) >> 8)
        WriteData[4] = UInt8((address & 0x00FF0000) >> 16)
        WriteData[5] = UInt8((address & 0xFF000000) >> 24)
        WriteData[6] = UInt8(size & 0x000000FF)
        WriteData[7] = UInt8((size & 0x0000FF00) >> 8)
        WriteData[8] = UInt8(check & 0x000000FF)
        WriteData[9] = UInt8((check & 0x0000FF00) >> 8)
        let writeData = NSData(bytes: &WriteData, length: WriteData.count) as Data
        self.writeDataToper(data: writeData)
    }
    
    fileprivate func OTA_Write_Section_All_Flash(){
        let srcPos = SendPacketID * MAX_TRANS_COUNT_V30
        var WriteData = Data()
        if (SendPacketID == SendPacketAllNum) {
            actionType = 0
        } else {
            if (SendPacketID == (SendPacketAllNum - 1)) {
                WriteData = ReadData.subdata(in: Range(NSRange.init(location: srcPos, length: (ReadData.count - srcPos)))!)//last a packet
                actionType = ACTIONTYPE_CMD_FW_UPGRADE//发送完最后一包了
            } else {
                WriteData = ReadData.subdata(in: Range(NSRange.init(location: srcPos, length: MAX_TRANS_COUNT_V30))!)//last a packet//other packet except first and last packet
            }
            OTA_Write_Secton_Flash(data: WriteData)
            SendPacketID += 1
        }
        if (SendPacketID % MAX_TRANS_SECTIONALL_PACKET_COUNT == 0) {
            actionType = ACTIONTYPE_CMD_FW_WRITE_END
            self.isCheckCRC = true
        } else {
            let progress = Float(SendPacketID) / Float(SendPacketAllNum)
            delegate?.otaLog(.inProgress(SendSectionID,SendPacketID), progress)
        }
    }
    fileprivate func OTA_Write_Secton_Flash(data:Data){
        self.writeDataToper(data: data)
    }
    fileprivate func OTA_Upgrade_Flash_V30(Size:Int,CRC:Int){
        let size = Int64(Size)
        var WriteData=[UInt8](repeating:0, count:8)
        WriteData[0] = CMD_FW_UPGRADEV20
        WriteData[1] = 0x04
        WriteData[2] = UInt8(size & 0x000000FF)
        WriteData[3] = UInt8((size & 0x0000FF00) >> 8)
        WriteData[4] = UInt8((size & 0x00FF0000) >> 16)
        WriteData[5] = UInt8((size & 0xFF000000) >> 24)
        WriteData[6] = UInt8(CRC & 0x000000FF)
        WriteData[7] = UInt8((CRC & 0x0000FF00) >> 8)
        actionType = ACTIONTYPE_CMD_FW_FINISH
        let writeData = NSData(bytes: &WriteData, length: WriteData.count) as Data
        self.isCheckCRC = true
        self.writeDataToper(data: writeData)
        delegate?.otaLog(.finish, 1.0)
    }
    /// 升级失败
    fileprivate func ota_failed(){
        delegate?.otaError(.interputed)
    }

    func didUpdateValue(for value: Data) {
        if (self.isCheckCRC) {
            OTA_Process_doing(data: value)
        }
    }
    
    func didWriteValue() {
        sendNextData()
    }
    
    fileprivate func sendNextData(){
        if (shouldAbort) {
            return
        }
        if self.isCheckCRC{
            delegate?.readOtaChar()
        }else{
            OTA_Process_doing(data: Data())
        }
    }
    
    fileprivate func writeDataToper(data:Data){
        self.writeData(data: data)
    }
    
    fileprivate func writeData(data:Data){
        self.delegate?.sendNext(data: data)
    }
}

//extension Data {
//    func md5Hash() -> String {
//            var digest = [UInt8](repeating: 0, count:Int(CC_MD5_DIGEST_LENGTH))
//            /// CC_MD5 performs digest calculation and places the result in the caller-supplied buffer for digest (md)
//            /// Calls the given closure with a pointer to the underlying unsafe bytes of the strData’s contiguous storage.
//            _ = self.withUnsafeBytes {
//                // CommonCrypto
//                // extern unsigned char *CC_MD5(const void *data, CC_LONG len, unsigned char *md) --|
//                // OpenSSL                                                                          |
//                // unsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md)        <-|
//                CC_MD5($0.baseAddress, UInt32(self.count), &digest)
//            }
//
//            var md5String = ""
//            /// Unpack each byte in the digest array and add them to the md5String
//            for byte in digest {
//                md5String += String(format:"%02x", UInt8(byte))
//            }
//            return md5String
//        }
//}
